Package ru.dubov.closestpair

Source Code of ru.dubov.closestpair.ClosestPair$PointComparatorY

package ru.dubov.closestpair;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

import ru.dubov.primitives.Point;

/**
* Finds the pair of closest points using different algorithms.
*
* @author Mikhail Dubov
*/
public class ClosestPair {
   
    /**
     * Finds the pair of closest points using a naive method in O(n^2).
     */
    public static ArrayList<Point> Naive(ArrayList<Point> points) {
        Point p0 = points.get(0);
        Point p1 = points.get(1);
       
        for (int i = 0; i < points.size() - 1; i++) {
            for (int j = i + 1; j < points.size(); j++) {
                if(dist(points.get(i), points.get(j)) < dist(p0, p1)) {
                    p0 = points.get(i);
                    p1 = points.get(j);
                }
            }
        }
       
        ArrayList<Point> resultPair = new ArrayList<Point>();
        resultPair.add(p0);
        resultPair.add(p1);
       
        return resultPair;
    }

    /**
     * Finds the pair of closest points using
     * a "divide and conquer" algorithm in O(n*log(n)).
     */
    public static ArrayList<Point> Fast(ArrayList<Point> points) {
        ArrayList<Point> X = (ArrayList<Point>)points.clone();
        ArrayList<Point> Y = (ArrayList<Point>)points.clone();

        Collections.sort(X, new PointComparatorX());
        Collections.sort(Y, new PointComparatorY());

        return ClosestPair(X, Y);
    }

    /**
     * The recursive procedure for the "Fast" algorithm.
     */
    private static ArrayList<Point> ClosestPair(ArrayList<Point> X, ArrayList<Point> Y) {
               
        ArrayList<Point> resultPair = new ArrayList<Point>();

        // База рекурсии - случай |P| <= 3
        if (X.size() <= 1) {
            resultPair = null;
        } else if (X.size() == 2) {
            resultPair = X;
        } else if (X.size() == 3) {
            // |P| == 3 => Грубый перебор
            double dist1 = dist(X.get(0), X.get(1));
            double dist2 = dist(X.get(0), X.get(2));
            double dist3 = dist(X.get(1), X.get(2));

            if (dist2 < dist3) {
                if (dist1 < dist2) {
                    resultPair.add(X.get(0));
                    resultPair.add(X.get(1));
                } else {
                    resultPair.add(X.get(0));
                    resultPair.add(X.get(2));
                }
            } else {
                if (dist1 < dist3) {
                    resultPair.add(X.get(0));
                    resultPair.add(X.get(1));
                } else {
                    resultPair.add(X.get(1));
                    resultPair.add(X.get(2));
                }
            }
        }
        // Шаг рекурсии - разделяй и властвуй
        else {
            double lX = (X.get(X.size() / 2)).getX();

            // Разделяй ...

            ArrayList<Point> XL = new ArrayList<Point>();
            ArrayList<Point> XR = new ArrayList<Point>();

            for (int i = 0; i < X.size(); i++) {
                if ((X.get(i)).getX() <= lX) {
                    XL.add(X.get(i));
                } else {
                    XR.add(X.get(i));
                }
            }

            ArrayList<Point> YL = new ArrayList<Point>();
            ArrayList<Point> YR = new ArrayList<Point>();

            for (int i = 0; i < Y.size(); i++) {
                if ((Y.get(i)).getX() <= lX) {
                    YL.add(Y.get(i));
                } else {
                    YR.add(Y.get(i));
                }
            }
           
            // Приходится специально обрабатывать вырожденный случай, когда
            // все точки уходят налево (например, если у них равны координаты X)
            // TODO: Потенциально деградация до O(n^2), если все точки на оси Y.
            if (XR.size() == 0) {
                // Пока что решение - просто перекидываем
                // крайнюю справа точку из XL направо, в XR
                Point repl = XL.get(XL.size()-1);
               
                XL.remove(repl);
                XR.add(repl);
               
                YL.remove(repl);
                YR.add(repl);
            }
           

            // ... Властвуй ...

            ArrayList<Point> ClosestLeft = ClosestPair(XL, YL);
            ArrayList<Point> ClosestRight = ClosestPair(XR, YR);
            ArrayList<Point> ClosestBetween = ClosestBetweenSubsets(X, Y,
                                                Math.min(dist(ClosestLeft), dist(ClosestRight)));

            double distLeft = dist(ClosestLeft);
            double distRight = dist(ClosestRight);
            double distBetween = dist(ClosestBetween);

            // ... Объединяй.

            if (distLeft < distRight) {
                if (distBetween < distLeft) {
                    resultPair = ClosestBetween;
                } else {
                    resultPair = ClosestLeft;
                }
            } else {
                if (distBetween < distRight) {
                    resultPair = ClosestBetween;
                } else {
                    resultPair = ClosestRight;
                }
            }
        }

        return resultPair;
    }

    private static ArrayList<Point> ClosestBetweenSubsets(ArrayList<Point> X, ArrayList<Point> Y, double d) {
        double lX = (X.get(X.size() / 2)).getX();

        // Заполняем Y2
        ArrayList<Point> Y2 = new ArrayList<Point>();
        for (int i = 0; i < Y.size(); i++) {
            if (Math.abs((Y.get(i)).getX() - lX) <= d) {
                Y2.add(Y.get(i));
            }
        }

        // Проходимся по Y2 в поисках ближайших точек
        if (Y2.size() < 2) {
            return null;
        }

        Point p1 = Y2.get(0);
        Point p2 = Y2.get(1);

        // Достаточно просмотреть 7 точек
        for (int i = 0; i < Y2.size() - 1; i++) {
            for (int j = i + 1; j <= Math.min(i + 8, Y2.size() - 1); j++)
                if (dist(Y2.get(i), Y2.get(j)) < dist(p1, p2)) {
                    p1 = Y2.get(i);
                    p2 = Y2.get(j);
                }
        }

        ArrayList resultPair = new ArrayList();
        resultPair.add(p1);
        resultPair.add(p2);

        return resultPair;
    }

    public static double dist(Point p0, Point p1) {
        return Math.sqrt((p1.getX() - p0.getX()) * (p1.getX() - p0.getX()) +
                         (p1.getY() - p0.getY()) * (p1.getY() - p0.getY()));
    }

    private static double dist(ArrayList<Point> pair) {
        if (pair == null || pair.size() != 2) {
            return Double.POSITIVE_INFINITY;
        } else {
            return Math.sqrt(((pair.get(1)).getX() - (pair.get(0)).getX()) * ((pair.get(1)).getX() - (pair.get(0)).getX()) +
                         ((pair.get(1)).getY() - (pair.get(0)).getY()) * ((pair.get(1)).getY() - (pair.get(0)).getY()));
        }
    }


    static class PointComparatorX implements Comparator<Point> {

         public int compare(Point p1, Point p2) {
            
             if (p1.getX() < p2.getX()) return -1;
             if (p1.getX() > p2.getX()) return 1;
             return 0;
         }
    }

    static class PointComparatorY implements Comparator<Point> {
       
        @Override
        public int compare(Point p1, Point p2) {
            
            if (p1.getY() < p2.getY()) return -1;
            if (p1.getY() > p2.getY()) return 1;
            return 0;
        }
    }
}
TOP

Related Classes of ru.dubov.closestpair.ClosestPair$PointComparatorY

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.